// --------------------------------------------------------------------------------------------------------------------
// <copyright file="ControllerStateMachineDescriptionTests.cs" company="Microsoft Corporation">
// The MIT License (MIT)
// 
// Copyright (c) 2014, Microsoft Corporation
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
// </copyright>
// --------------------------------------------------------------------------------------------------------------------

namespace Test.Robotics.Manipulation
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;

    using Microsoft.Robotics.Manipulation.ControllerStateMachine;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    /// <summary>
    /// Tests for <see cref="ControllerStateMachineDescription"/> class.
    /// </summary>
    [TestClass]
    public class ControllerStateMachineDescriptionTests
    {
        /// <summary>
        /// Test constructor with value of 'null' for operands array
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void ControllerOperationConstructorNullOperands()
        {
            ControllerOperation controllerOperation = new ControllerOperation(0, new RegisterID(), null);
            Assert.AreEqual(0, controllerOperation.NumOperands, "Number of controller operations is 0");
            Assert.AreEqual(RegisterTypes.Invalid, controllerOperation.Operand0.RegisterType, "Operand0 register type should be invalid");
        }

        /// <summary>
        /// Test that an exception is thrown when ControllerOperations constructor is called with more operands than are supported.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        [ExpectedException(typeof(ArgumentException))]
        public void ControllerOperationConstructorTooManyOperands()
        {
            ControllerOperation controllerOperation = new ControllerOperation(0, new RegisterID(), new RegisterID[ControllerStateMachineDescriptionConstants.MaxOperandsPerOperation + 1]);
        }

        /// <summary>
        /// Test that an exception is thrown when OperationList constructor is called with more operations than are supported.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        [ExpectedException(typeof(ArgumentException))]
        public void OperationListConstructorTooManyOperations()
        {
            OperationList operationList = new OperationList(new ControllerOperation[ControllerStateMachineDescriptionConstants.MaxOperationsPerState + 1]);
        }

        /// <summary>
        /// Test that constructor to OperationList sets values properly.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void OperationListConstructor()
        {
            ControllerOperation[] controllerOperations = new ControllerOperation[3] 
            {
                new ControllerOperation(0, new RegisterID(), null),
                new ControllerOperation(1, new RegisterID(), null),
                new ControllerOperation(2, new RegisterID(), null),
            };

            OperationList operationList = new OperationList(controllerOperations);

            Assert.AreEqual(0, operationList.Operation0.OpCode, "Operation 0 has proper op code");
            Assert.AreEqual(1, operationList.Operation1.OpCode, "Operation 1 has proper op code");
            Assert.AreEqual(2, operationList.Operation2.OpCode, "Operation 2 has proper op code");
        }

        /// <summary>
        /// Test that constructor to OperationList handles 'null' for test operands correctly.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void ControllerTransitionConstructorNullTestOperands()
        {
            ControllerTransition controllerTransition = new ControllerTransition(0, 0, null, new OperationList());
            Assert.AreEqual(0, controllerTransition.NumTestOperands, "Number of test operands is 0");
            Assert.AreEqual(RegisterTypes.Invalid, controllerTransition.TestOperand0.RegisterType, "TestOperand0 register type should be invalid");
        }

        /// <summary>
        /// Test that an exception is thrown when ControllerTransition constructor is called with more test operands than are supported.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        [ExpectedException(typeof(ArgumentException))]
        public void ControllerTransitionConstructorTooManyOperands()
        {
            ControllerTransition controllerTransition = new ControllerTransition(0, 0, new RegisterID[ControllerStateMachineDescriptionConstants.MaxTestOperands + 1], new OperationList());
        }

        /// <summary>
        /// Test that constructor for ControllerState handles 'null' for transitions properly
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void ControllerStateConstructorNullTestOperands()
        {
            ControllerState controllerState = new ControllerState(0, 0, new OperationList(), null);
            Assert.AreEqual(0, controllerState.NumTransitions, "Number of transitions operands is 0");
        }

        /// <summary>
        /// Test that an exception is thrown when ControllerState constructor is called with more transitions than are supported.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        [ExpectedException(typeof(ArgumentException))]
        public void ControllerStateConstructorTooManyTransitions()
        {
            ControllerState controllerState = new ControllerState(0, 0, new OperationList(), new ControllerTransition[ControllerStateMachineDescriptionConstants.MaxNumberOfTransitions + 1]);
        }

        /// <summary>
        /// Test that an exception is thrown when ControllerStateMachineDescription constructor is called with an empty array of states.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        [ExpectedException(typeof(ArgumentException))]
        public void EmptyControllerStateMachineDescription()
        {
            ControllerStateMachineDescription desc = new ControllerStateMachineDescription(new ControllerState[0]);
        }

        /// <summary>
        /// Test that an exception is thrown when ControllerStateMachineDescription constructor is called with more states than are supported.
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        [ExpectedException(typeof(ArgumentException))]
        public void ControllerStateMachineDescriptionConstructorTooManyStates()
        {
            ControllerStateMachineDescription desc = new ControllerStateMachineDescription(new ControllerState[ControllerStateMachineDescriptionConstants.MaxNumberOfStates + 1]);
        }

        /// <summary>
        /// Test that ToControllerStateMachineDescription() method of ControllerStateMachine applys transitions in correct order
        /// </summary>
        [TestMethod]
        [TestCategory("Unit")]
        [Priority(0)]
        public void ToControllerStateMachineDescriptionTransitionOrder()
        {
            RegisterID someRegister = RegisterID.NewFeedbackRegister(0);
            RegisterID[] registerIDArray = new RegisterID[1]
            {
                someRegister
            };

            State[] states = new State[2]
            {
                new State(0, new ControllerOperation[0], null, null),
                new State(0, new ControllerOperation[0], null, null)
            };

            Transition trans1 = new Transition(states[0], 1, registerIDArray, null);
            Transition trans2 = new Transition(states[1], 2, registerIDArray, null);

            states[0].TransitionsToEvaluateBeforeDefaults = new Transition[1]
            {
                trans1
            };

            states[0].TransitionsToEvaluateAfterDefaults = new Transition[1]
            {
                trans2
            };

            states[1].TransitionsToEvaluateBeforeDefaults = new Transition[1]
            {
                trans2
            };

            states[1].TransitionsToEvaluateAfterDefaults = new Transition[1]
            {
                trans1
            };

            ControllerStateMachine csm = new ControllerStateMachine();
            csm.States = new List<State>(states);

            ControllerStateMachineDescription csmd = csm.ToControllerStateMachineDescription();

            Assert.AreEqual(1, csmd.State0.Transition0.TestOpCode, "First transition of state 0 is correct");
            Assert.AreEqual(2, csmd.State0.Transition1.TestOpCode, "Second transition of state 0 is correct");
            Assert.AreEqual(2, csmd.State1.Transition0.TestOpCode, "First transition of state 1 is correct");
            Assert.AreEqual(1, csmd.State1.Transition1.TestOpCode, "Second transition of state 1 is correct");
        }
    }
}
